﻿' MainForm.vb
Imports System.ComponentModel
Imports System.IO
Imports System.Security.Policy
Imports System.Threading
Imports Microsoft.Win32
Imports TodoList.Models

Public Class SearchMainForm
    Private ReadOnly _indexBuilder As New IndexBuilder()
    Private ReadOnly _searchEngine As New SearchEngine(_indexBuilder)
    Private _lastSearchResults As List(Of SearchResult)
    Private _searchBackgroundWorker As BackgroundWorker
    Private _indexBackgroundWorker As BackgroundWorker

    ' 成员变量
    Private statusStrip As StatusStrip
    Private statusLabel As ToolStripStatusLabel
    Private progressBar As ToolStripProgressBar
    Private toolStrip As ToolStrip
    Private searchBox1 As ToolStripTextBox

    '启动加载用
    Private _splash As Form
    '定时更新用
    Private _updateTimer As Timer
    '是否正在后台更新
    Private _isUpdating As Boolean = False

    Private _suppressUiUpdates As Boolean = False

    Public Sub New()
        InitializeComponent()
        SetupUI()
        SetupBackgroundWorkers()

        'LoadSettings()
        '' 检查Tesseract训练数据
        'CheckTesseractData()
    End Sub

    Private Sub CheckTesseractData()
        Dim tessDataPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tessdata")
        Dim engDataFile = Path.Combine(tessDataPath, "eng.traineddata")
        Dim chiDataFile = Path.Combine(tessDataPath, "chi_sim.traineddata")

        If Not File.Exists(engDataFile) Or Not File.Exists(chiDataFile) Then
            Dim result = MessageBox.Show(
            "检测到缺少OCR识别所需的语言数据文件。您需要下载这些文件才能使用图片文字识别功能。" & Environment.NewLine &
            Environment.NewLine &
            "请从以下链接下载语言数据文件：" & Environment.NewLine &
            "https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata" & Environment.NewLine &
            "https://github.com/tesseract-ocr/tessdata/raw/main/chi_sim.traineddata" & Environment.NewLine &
            Environment.NewLine &
            "下载后请将文件放到程序目录下的tessdata文件夹中。" & Environment.NewLine &
            Environment.NewLine &
            "是否现在打开下载页面？",
            "需要OCR语言数据",
            MessageBoxButtons.YesNo,
            MessageBoxIcon.Information)

            If result = DialogResult.Yes Then
                Process.Start("https://github.com/tesseract-ocr/tessdata/")
            End If
        End If
    End Sub

    Private Sub SetupUI()
        ' 主窗体设置
        Me.Text = "文件内容检索工具"
        Me.Size = New Size(1000, 700)
        Me.MinimumSize = New Size(800, 600)

        '创建控件 -注意添加顺序很重要

        ' 1. 首先创建菜单栏（顶部）
        Dim menuStrip As New MenuStrip With {
             .Name = "menuStrip",
             .Dock = DockStyle.Top
        }
        Me.MainMenuStrip = menuStrip

        ' 文件菜单
        Dim fileMenu As New ToolStripMenuItem("文件")
        Dim indexMenuItem As New ToolStripMenuItem("创建索引...", Nothing, AddressOf IndexMenuItem_Click)
        Dim addMenuItem As New ToolStripMenuItem("添加索引...", Nothing, AddressOf AddMenuItem_Click)
        Dim updateMenuItem As New ToolStripMenuItem("更新索引", Nothing, AddressOf UpdateMenuItem_Click)
        Dim exitMenuItem As New ToolStripMenuItem("退出", Nothing, AddressOf ExitMenuItem_Click)
        fileMenu.DropDownItems.AddRange({indexMenuItem, updateMenuItem, New ToolStripSeparator(), exitMenuItem})
        fileMenu.DropDownItems.Insert(1, addMenuItem) ' 插到“创建索引...”后面

        ' 工具菜单 - 添加OCR设置
        Dim toolsMenu As New ToolStripMenuItem("工具")
        Dim ocrSettingsMenuItem As New ToolStripMenuItem("OCR设置...", Nothing, AddressOf OcrSettingsMenuItem_Click)
        Dim statsMenuItem As New ToolStripMenuItem("索引统计", Nothing, AddressOf StatsMenuItem_Click)
        toolsMenu.DropDownItems.AddRange({ocrSettingsMenuItem, statsMenuItem})

        menuStrip.Items.AddRange({fileMenu, toolsMenu})

        ' 2. 创建工具栏（菜单下方）
        Dim toolStrip As New ToolStrip With {
            .Name = "toolStrip",
            .Dock = DockStyle.Top
        }

        toolStrip.Items.Add(New ToolStripButton("创建索引", Nothing, AddressOf IndexMenuItem_Click))
        toolStrip.Items.Add(New ToolStripButton("添加索引", Nothing, AddressOf AddMenuItem_Click))
        toolStrip.Items.Add(New ToolStripButton("更新索引", Nothing, AddressOf UpdateMenuItem_Click))
        toolStrip.Items.Add(New ToolStripSeparator())

        ' 搜索框
        Dim searchLabel As New ToolStripLabel("搜索:")
        toolStrip.Items.Add(searchLabel)

        searchBox1 = New ToolStripTextBox With {
            .Name = "searchBox",
            .Width = 300
        }
        AddHandler searchBox1.KeyDown, AddressOf SearchBox_KeyDown
        toolStrip.Items.Add(searchBox1)

        Dim searchButton As New ToolStripButton("查找", Nothing, AddressOf SearchButton_Click)
        toolStrip.Items.Add(searchButton)

        ' 3. 创建状态栏（底部）
        statusStrip = New StatusStrip With {
            .Name = "statusStrip",
            .Dock = DockStyle.Bottom
        }
        progressBar = New ToolStripProgressBar With {
        .Name = "progressBar",
        .Visible = False
    }
        statusStrip.Items.Add(progressBar)

        statusLabel = New ToolStripStatusLabel("就绪") With {
            .Name = "statusLabel"
        }
        statusStrip.Items.Add(statusLabel)

        '4. 创建主容器面板
        ' 使用TableLayoutPanel代替Panel，可以更好地控制布局
        Dim mainPanel As New TableLayoutPanel With {
            .Name = "mainPanel",
            .Dock = DockStyle.Fill,
            .RowCount = 1,
            .ColumnCount = 1
        }
        mainPanel.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
        mainPanel.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))

        ' 5. 创建分割面板
        Dim splitContainer As New SplitContainer With {
            .Name = "splitContainer",
            .Dock = DockStyle.Fill,
            .Orientation = Orientation.Vertical,
            .SplitterDistance = 300,
            .Panel1MinSize = 50,
            .Panel2MinSize = 50
        }

        ' 6. 创建结果列表视图
        Dim resultsListView As New ListView With {
            .Name = "resultsListView",
            .View = View.Details,
            .FullRowSelect = True,
            .GridLines = True,
            .Dock = DockStyle.Fill,
            .Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right
        }

        resultsListView.Columns.Add("文件名", 250)
        resultsListView.Columns.Add("路径", 300)
        resultsListView.Columns.Add("大小", 80, HorizontalAlignment.Right)
        resultsListView.Columns.Add("修改日期", 150)
        resultsListView.Columns.Add("得分", 60, HorizontalAlignment.Right)

        AddHandler resultsListView.SelectedIndexChanged, AddressOf ResultsListView_SelectedIndexChanged
        AddHandler resultsListView.DoubleClick, AddressOf ResultsListView_DoubleClick

        ' 创建右键菜单
        Dim contextMenu As New ContextMenuStrip()
        Dim openMenuItem As New ToolStripMenuItem("打开文件", Nothing, AddressOf OpenMenuItem_Click)
        Dim openFolderMenuItem As New ToolStripMenuItem("打开文件位置", Nothing, AddressOf OpenFolderMenuItem_Click)
        contextMenu.Items.AddRange({openMenuItem, openFolderMenuItem})
        resultsListView.ContextMenuStrip = contextMenu

        ' 7. 创建预览面板
        Dim previewPanel As New TableLayoutPanel With {
            .Name = "previewPanel",
            .Dock = DockStyle.Fill,
            .RowCount = 2,
            .ColumnCount = 1
        }
        previewPanel.RowStyles.Add(New RowStyle(SizeType.Absolute, 25))
        previewPanel.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
        previewPanel.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))

        Dim previewLabel As New Label With {
            .Text = "文件预览:",
            .Dock = DockStyle.Fill,
            .AutoSize = False
        }

        Dim previewBox As New RichTextBox With {
            .Name = "previewBox",
            .Dock = DockStyle.Fill,
            .ReadOnly = True,
            .BackColor = Color.White,
            .Font = New Font("Consolas", 10),
            .WordWrap = True
        }

        ' 8. 按照正确的层次结构添加控件
        ' 重要：先添加底层控件，再添加上层控件

        ' 添加预览面板的子控件
        previewPanel.Controls.Add(previewLabel, 0, 0)
        previewPanel.Controls.Add(previewBox, 0, 1)

        ' 添加分割面板的子控件
        splitContainer.Panel1.Controls.Add(resultsListView)
        splitContainer.Panel2.Controls.Add(previewPanel)

        ' 添加主面板的子控件
        mainPanel.Controls.Add(splitContainer, 0, 0)

        ' 按照自下而上的顺序添加主要控件到窗体
        ' 添加顺序很重要！从底层到顶层
        Me.Controls.Add(mainPanel)     ' 先添加内容区域
        Me.Controls.Add(statusStrip)   ' 再添加状态栏
        Me.Controls.Add(toolStrip)     ' 再添加工具栏
        Me.Controls.Add(menuStrip)     ' 最后添加菜单栏

        Me.PerformLayout()
        Me.Refresh()

    End Sub

    Private Sub ShowProgress(visible As Boolean)
        progressBar.Visible = visible
    End Sub

    Private Sub SetProgressValue(value As Integer)
        progressBar.Value = value
    End Sub

    ' ADD: “添加索引...”菜单/工具栏按钮事件
    Private Sub AddMenuItem_Click(sender As Object, e As EventArgs)
        Dim folderDialog As New FolderBrowserDialog()
        folderDialog.Description = "选择要添加到索引的文件夹"

        If folderDialog.ShowDialog() = DialogResult.OK Then
            Dim selectedPath = folderDialog.SelectedPath
            If Not String.IsNullOrEmpty(selectedPath) Then
                StartIndexing(selectedPath, IndexOperation.AddOrUpdate)
            End If
        End If
    End Sub

    ' 添加OCR设置对话框
    Private Sub OcrSettingsMenuItem_Click(sender As Object, e As EventArgs)
        Dim settingsForm As New Form With {
            .Text = "OCR设置",
            .Size = New Size(500, 300),
            .FormBorderStyle = FormBorderStyle.FixedDialog,
            .StartPosition = FormStartPosition.CenterParent,
            .MaximizeBox = False,
            .MinimizeBox = False
        }

        ' 语言选择
        Dim languageLabel As New Label With {
            .Text = "OCR语言:",
            .Location = New Point(20, 20),
            .Size = New Size(100, 20)
        }

        Dim languageCombo As New ComboBox With {
            .Location = New Point(130, 20),
            .Size = New Size(320, 20),
            .DropDownStyle = ComboBoxStyle.DropDownList
        }
        languageCombo.Items.AddRange({"英文 (eng)", "简体中文 (chi_sim)", "繁体中文 (chi_tra)", "英文+简体中文 (eng+chi_sim)"})
        languageCombo.SelectedIndex = 3 ' 默认选择英文+简体中文

        ' 图片大小限制
        Dim sizeLabel As New Label With {
            .Text = "处理图片大小限制:",
            .Location = New Point(20, 60),
            .Size = New Size(120, 20)
        }

        Dim sizeCombo As New ComboBox With {
            .Location = New Point(150, 60),
            .Size = New Size(300, 20),
            .DropDownStyle = ComboBoxStyle.DropDownList
        }
        sizeCombo.Items.AddRange({"无限制", "≤ 1MB", "≤ 5MB", "≤ 10MB", "≤ 20MB"})
        sizeCombo.SelectedIndex = 3 ' 默认10MB

        ' 确认按钮
        Dim okButton As New Button With {
            .Text = "确定",
            .DialogResult = DialogResult.OK,
            .Location = New Point(280, 220),
            .Size = New Size(80, 30)
        }

        ' 取消按钮
        Dim cancelButton As New Button With {
            .Text = "取消",
            .DialogResult = DialogResult.Cancel,
            .Location = New Point(380, 220),
            .Size = New Size(80, 30)
        }

        ' 添加控件到窗体
        settingsForm.Controls.AddRange({languageLabel, languageCombo, sizeLabel, sizeCombo, okButton, cancelButton})
        settingsForm.AcceptButton = okButton
        settingsForm.CancelButton = cancelButton

        ' 显示对话框
        If settingsForm.ShowDialog() = DialogResult.OK Then
            ' 保存设置
            My.Settings.OcrLanguage = languageCombo.SelectedItem.ToString().Split(" "c)(1).Trim("()")

            ' 解析大小限制
            Dim sizeLimit As Integer = 0
            Select Case sizeCombo.SelectedIndex
                Case 0 ' 无限制
                    sizeLimit = 0
                Case 1 ' 1MB
                    sizeLimit = 1
                Case 2 ' 5MB
                    sizeLimit = 5
                Case 3 ' 10MB
                    sizeLimit = 10
                Case 4 ' 20MB
                    sizeLimit = 20
            End Select

            My.Settings.OcrSizeLimit = sizeLimit
            My.Settings.Save()

            MessageBox.Show("OCR设置已保存。新设置将在下次创建或更新索引时生效。", "设置已保存", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If
    End Sub

    Private Sub SetupBackgroundWorkers()
        ' 搜索后台工作线程
        _searchBackgroundWorker = New BackgroundWorker With {
            .WorkerReportsProgress = True,
            .WorkerSupportsCancellation = True
        }

        AddHandler _searchBackgroundWorker.DoWork, AddressOf SearchBackgroundWorker_DoWork
        AddHandler _searchBackgroundWorker.ProgressChanged, AddressOf BackgroundWorker_ProgressChanged
        AddHandler _searchBackgroundWorker.RunWorkerCompleted, AddressOf SearchBackgroundWorker_RunWorkerCompleted

        ' 索引后台工作线程
        _indexBackgroundWorker = New BackgroundWorker With {
            .WorkerReportsProgress = True,
            .WorkerSupportsCancellation = True
        }

        AddHandler _indexBackgroundWorker.DoWork, AddressOf IndexBackgroundWorker_DoWork
        AddHandler _indexBackgroundWorker.ProgressChanged, AddressOf BackgroundWorker_ProgressChanged
        AddHandler _indexBackgroundWorker.RunWorkerCompleted, AddressOf IndexBackgroundWorker_RunWorkerCompleted
    End Sub

    Private Sub LoadSettings()
        ' 尝试加载现有索引
        Try
            _indexBuilder.LoadIndex()
            UpdateStatusInfo()
        Catch ex As Exception
            MessageBox.Show("加载索引失败: " & ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    ' 修改 UpdateStatusInfo 方法以显示 OCR 相关信息
    Private Sub UpdateStatusInfo()
        Dim stats = _indexBuilder.GetIndexStats()
        'Dim statusLabel As ToolStripStatusLabel = DirectCast(Me.Controls.Find("statusLabel", True)(0), ToolStripStatusLabel)
        Dim rootCount = _indexBuilder.GetIndexedRoots().Count

        If stats.TotalFiles > 0 Then
            Dim sizeInMB = stats.TotalSize / (1024 * 1024)
            Dim lastIndexed = If(stats.IndexedDate.HasValue, stats.IndexedDate.Value.ToString("yyyy-MM-dd HH:mm:ss"), "从未")

            ' 计算OCR文件数量
            Dim ocrFilesCount As Integer = 0
            For Each fileInfo In _indexBuilder.GetIndex().Values
                If IsImageFile(fileInfo.Extension) AndAlso Not String.IsNullOrEmpty(fileInfo.Content) Then
                    ocrFilesCount += 1
                End If
            Next

            ' 创建OCR状态文本
            Dim ocrStatus As String = If(ocrFilesCount > 0, $" | OCR处理文件: {ocrFilesCount}", "")

            statusLabel.Text = $"已索引 {stats.TotalFiles} 个文件 ({sizeInMB:F2} MB){ocrStatus} | 上次索引时间: {lastIndexed} "

            statusLabel.Text = statusLabel.Text + $" | 源目录: {rootCount}"
        Else
            statusLabel.Text = "未创建索引"
        End If
    End Sub

    Private Function IsImageFile(extension As String) As Boolean
        Dim imageExtensions As String() = {".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff", ".gif"}
        Return imageExtensions.Contains(extension.ToLower())
    End Function

    ' 菜单事件处理
    Private Sub IndexMenuItem_Click(sender As Object, e As EventArgs)
        Dim folderDialog As New FolderBrowserDialog()
        folderDialog.Description = "选择要索引的文件夹"

        If folderDialog.ShowDialog() = DialogResult.OK Then
            Dim selectedPath = folderDialog.SelectedPath
            If Not String.IsNullOrEmpty(selectedPath) Then
                ' 开始索引创建
                StartIndexing(selectedPath, IndexOperation.Build) 'False)
            End If
        End If
    End Sub

    Private Sub UpdateMenuItem_Click(sender As Object, e As EventArgs)
        Dim stats = _indexBuilder.GetIndexStats()
        If stats.TotalFiles = 0 Then
            MessageBox.Show("尚未创建索引，请先创建索引。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Return
        End If

        Dim folderDialog As New FolderBrowserDialog()
        folderDialog.Description = "选择要更新索引的文件夹"

        If folderDialog.ShowDialog() = DialogResult.OK Then
            Dim selectedPath = folderDialog.SelectedPath
            If Not String.IsNullOrEmpty(selectedPath) Then
                ' 开始索引更新
                StartIndexing(selectedPath, IndexOperation.Update) 'True)
            End If
        End If
    End Sub

    Private Sub ExitMenuItem_Click(sender As Object, e As EventArgs)
        Me.Close()
    End Sub

    Private Sub StatsMenuItem_Click(sender As Object, e As EventArgs)
        Dim stats = _indexBuilder.GetIndexStats()
        Dim sizeInMB = stats.TotalSize / (1024 * 1024)
        Dim lastIndexed = If(stats.IndexedDate.HasValue, stats.IndexedDate.Value.ToString("yyyy-MM-dd HH:mm:ss"), "从未")

        MessageBox.Show(
            $"索引统计信息:{Environment.NewLine}" &
            $"索引文件数量: {stats.TotalFiles}{Environment.NewLine}" &
            $"索引文件总大小: {sizeInMB:F2} MB{Environment.NewLine}" &
            $"上次索引时间: {lastIndexed}",
            "索引统计",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information)
    End Sub

    ' 搜索相关事件
    Private Sub SearchBox_KeyDown(sender As Object, e As KeyEventArgs)
        If e.KeyCode = Keys.Enter Then
            e.SuppressKeyPress = True
            StartSearch()
        End If
    End Sub

    Private Sub SearchButton_Click(sender As Object, e As EventArgs)
        StartSearch()
    End Sub

    Private Sub StartSearch()
        If _searchBackgroundWorker.IsBusy Then
            Return
        End If

        'Dim searchBox As ToolStripTextBox = DirectCast(Me.Controls.Find("searchBox", True)(0), ToolStripTextBox)
        Dim query = searchBox1.Text.Trim()

        If String.IsNullOrEmpty(query) Then
            MessageBox.Show("请输入搜索关键词", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Return
        End If

        ' 清空结果列表
        Dim resultsListView As ListView = DirectCast(Me.Controls.Find("resultsListView", True)(0), ListView)
        resultsListView.Items.Clear()

        ' 清空预览框
        Dim previewBox As RichTextBox = DirectCast(Me.Controls.Find("previewBox", True)(0), RichTextBox)
        previewBox.Clear()

        ' 显示进度条
        'Dim progressBar As ToolStripProgressBar = DirectCast(Me.Controls.Find("progressBar", True)(0), ToolStripProgressBar)
        progressBar.Value = 0
        progressBar.Visible = True

        ' 更新状态
        'Dim statusLabel As ToolStripStatusLabel = DirectCast(Me.Controls.Find("statusLabel", True)(0), ToolStripStatusLabel)
        statusLabel.Text = "正在搜索..."

        ' 开始后台搜索
        _searchBackgroundWorker.RunWorkerAsync(query)
    End Sub

    Private Sub SearchBackgroundWorker_DoWork(sender As Object, e As DoWorkEventArgs)
        Dim query As String = DirectCast(e.Argument, String)

        ' 执行搜索
        Dim searchResults1 = _searchEngine.Search(query)

        ' 创建一个新的临时列表
        Dim tempResults As New List(Of SearchResult)()
        For Each result In searchResults1
            tempResults.Add(result)
        Next

        e.Result = New SearchResults(query, tempResults)

        ' 报告进度
        _searchBackgroundWorker.ReportProgress(100)
    End Sub

    Private Sub SearchBackgroundWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
        ' 隐藏进度条
        'Dim progressBar As ToolStripProgressBar = DirectCast(Me.Controls.Find("progressBar", True)(0), ToolStripProgressBar)
        progressBar.Visible = False

        If e.Error IsNot Nothing Then
            MessageBox.Show("搜索出错: " & e.Error.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        Dim searchResults As SearchResults = DirectCast(e.Result, SearchResults)
        _lastSearchResults = searchResults.Results

        ' 更新结果列表
        Dim resultsListView As ListView = DirectCast(Me.Controls.Find("resultsListView", True)(0), ListView)
        resultsListView.BeginUpdate()
        resultsListView.Items.Clear()


        ' 统计OCR结果数量
        Dim ocrResultsCount As Integer = 0

        For Each result In _lastSearchResults
            Dim fileInfo = result.File
            Dim item As New ListViewItem(fileInfo.Name)

            ' 如果是OCR结果，使用特殊图标或改变背景色
            If result.IsOcrResult Then
                item.BackColor = Color.LightYellow
                item.Text = "📷 " & fileInfo.Name  ' 添加相机图标前缀
                ocrResultsCount += 1
            End If

            item.SubItems.Add(fileInfo.Path)
            item.SubItems.Add(FormatFileSize(fileInfo.Size))
            item.SubItems.Add(fileInfo.Modified.ToString("yyyy-MM-dd HH:mm:ss"))
            item.SubItems.Add(result.Score.ToString("F1"))
            item.Tag = result  ' 存储整个SearchResult对象

            resultsListView.Items.Add(item)

        Next

        resultsListView.EndUpdate()

        ' 更新状态
        'Dim statusLabel As ToolStripStatusLabel = DirectCast(Me.Controls.Find("statusLabel", True)(0), ToolStripStatusLabel)
        Dim ocrInfo = If(ocrResultsCount > 0, $" (包含 {ocrResultsCount} 个图片OCR结果)", "")
        statusLabel.Text = $"找到 {_lastSearchResults.Count} 个结果{ocrInfo}  "
    End Sub

    ' 结果列表事件
    Private Sub ResultsListView_SelectedIndexChanged(sender As Object, e As EventArgs)
        Dim resultsListView As ListView = DirectCast(sender, ListView)
        If resultsListView.SelectedItems.Count = 0 Then
            Return
        End If

        Dim selectedItem = resultsListView.SelectedItems(0)
        Dim searchResult As SearchResult = DirectCast(selectedItem.Tag, SearchResult)
        Dim fileInfo = searchResult.File

        ' 更新预览框
        Dim previewBox As RichTextBox = DirectCast(Me.Controls.Find("previewBox", True)(0), RichTextBox)
        previewBox.Clear()

        ' 对于图片文件，显示OCR文本并提供图片预览选项
        If searchResult.IsOcrResult Then
            previewBox.Text = "【图片OCR文本】" & Environment.NewLine & Environment.NewLine

            If searchResult.MatchedLines.Count > 0 Then
                previewBox.Text += String.Join(Environment.NewLine & Environment.NewLine, searchResult.MatchedLines)
            ElseIf Not String.IsNullOrEmpty(fileInfo.Content) Then
                previewBox.Text += fileInfo.Content.Replace("[图片OCR文本] ", "")
            Else
                previewBox.Text += "[无法提取图片文本]"
            End If

            ' 添加查看图片选项
            previewBox.Text += Environment.NewLine & Environment.NewLine &
                          "----------------------------" & Environment.NewLine &
                          "双击此处查看原始图片"

            ' 高亮匹配的关键词
            'Dim searchBox As ToolStripTextBox = DirectCast(Me.Controls.Find("searchBox", True)(0), ToolStripTextBox)
            HighlightKeywords(previewBox, searchBox1.Text)

            ' 添加双击事件以查看图片
            AddHandler previewBox.DoubleClick, AddressOf PreviewBox_DoubleClick
        Else
            ' 对于非图片文件，使用原有逻辑
            If String.IsNullOrEmpty(fileInfo.Content) Then
                previewBox.Text = $"[无法预览此文件类型: {fileInfo.Extension}]"
                Return
            End If

            ' 查找当前搜索结果中的匹配行
            If searchResult.MatchedLines.Count > 0 Then
                ' 显示匹配行
                previewBox.Text = String.Join(Environment.NewLine & Environment.NewLine, searchResult.MatchedLines)
            Else
                ' 显示文件开头部分
                Dim previewContent = fileInfo.Content
                If previewContent.Length > 5000 Then
                    previewContent = previewContent.Substring(0, 5000) & "..."
                End If
                previewBox.Text = previewContent
            End If

            ' 高亮匹配的关键词
            'Dim searchBox As ToolStripTextBox = DirectCast(Me.Controls.Find("searchBox", True)(0), ToolStripTextBox)
            HighlightKeywords(previewBox, searchBox1.Text)

            ' 移除双击事件（如果之前添加过）
            RemoveHandler previewBox.DoubleClick, AddressOf PreviewBox_DoubleClick
        End If
    End Sub

    ' 添加图片预览处理
    Private Sub PreviewBox_DoubleClick(sender As Object, e As EventArgs)
        Dim resultsListView As ListView = DirectCast(Me.Controls.Find("resultsListView", True)(0), ListView)
        If resultsListView.SelectedItems.Count = 0 Then
            Return
        End If

        Dim selectedItem = resultsListView.SelectedItems(0)
        Dim searchResult As SearchResult = DirectCast(selectedItem.Tag, SearchResult)

        ' 打开图片查看器
        Try
            Process.Start(searchResult.File.Path)
        Catch ex As Exception
            MessageBox.Show("无法打开图片: " & ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub HighlightKeywords(rtb As RichTextBox, keywords As String)
        If String.IsNullOrEmpty(keywords) OrElse String.IsNullOrEmpty(rtb.Text) Then
            Return
        End If

        ' 分词
        Dim terms = keywords.Split(" "c).Where(Function(t) t.Length > 1).ToList()
        If terms.Count = 0 Then
            Return
        End If

        Dim originalText = rtb.Text
        rtb.Text = originalText

        ' 高亮关键词
        For Each term In terms
            Dim startIndex As Integer = 0
            While startIndex < rtb.TextLength
                Dim foundIndex = rtb.Text.IndexOf(term, startIndex, StringComparison.OrdinalIgnoreCase)
                If foundIndex < 0 Then
                    Exit While
                End If

                rtb.SelectionStart = foundIndex
                rtb.SelectionLength = term.Length
                rtb.SelectionBackColor = Color.Yellow

                startIndex = foundIndex + term.Length
            End While
        Next

        rtb.SelectionStart = 0
        rtb.SelectionLength = 0
    End Sub

    Private Sub ResultsListView_DoubleClick(sender As Object, e As EventArgs)
        OpenSelectedFile()
    End Sub

    Private Sub OpenMenuItem_Click(sender As Object, e As EventArgs)
        OpenSelectedFile()
    End Sub

    Private Sub OpenFolderMenuItem_Click(sender As Object, e As EventArgs)
        Dim resultsListView As ListView = DirectCast(Me.Controls.Find("resultsListView", True)(0), ListView)
        If resultsListView.SelectedItems.Count = 0 Then
            Return
        End If

        Dim selectedItem = resultsListView.SelectedItems(0)
        'Dim fileInfo As FileItem = DirectCast(selectedItem.Tag, FileItem)
        Dim searchResult As SearchResult = DirectCast(selectedItem.Tag, SearchResult)
        Dim fileInfo As FileItem = searchResult.File

        Try
            Process.Start("explorer.exe", "/select," & fileInfo.Path)
        Catch ex As Exception
            MessageBox.Show("无法打开文件位置: " & ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub OpenSelectedFile()
        Dim resultsListView As ListView = DirectCast(Me.Controls.Find("resultsListView", True)(0), ListView)
        If resultsListView.SelectedItems.Count = 0 Then
            Return
        End If

        Dim selectedItem = resultsListView.SelectedItems(0)

        Dim searchResult As SearchResult = DirectCast(selectedItem.Tag, SearchResult)
        Dim fileInfo As FileItem = searchResult.File

        ' 验证文件路径
        If Not File.Exists(fileInfo.Path) Then
            MessageBox.Show($"找不到文件: {fileInfo.Path}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        Try
            ' 使用新的方法打开文件
            Dim processInfo As New ProcessStartInfo()
            processInfo.FileName = fileInfo.Path
            processInfo.UseShellExecute = True  ' 这是关键设置！使用系统关联的应用程序打开文件
            processInfo.Verb = "open"

            Process.Start(processInfo)
        Catch ex As Exception
            ' 捕获并显示详细的错误消息
            MessageBox.Show($"无法打开文件: {ex.Message}{Environment.NewLine}文件路径: {fileInfo.Path}",
                      "文件打开错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Try
                ' 使用默认的系统命令打开文件所在的文件夹并选中该文件
                Process.Start("explorer.exe", $"/select,{fileInfo.Path}")
            Catch innerEx As Exception
                MessageBox.Show($"无法打开文件位置: {innerEx.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End Try
        End Try
    End Sub

    ' 索引创建和更新
    Private Sub StartIndexing(rootDirectory As String, op As IndexOperation)  'isUpdate As Boolean)
        If _indexBackgroundWorker.IsBusy Then
            Return
        End If

        ' 显示进度条
        'Dim progressBar As ToolStripProgressBar = DirectCast(Me.Controls.Find("progressBar", True)(0), ToolStripProgressBar)
        progressBar.Value = 0
        progressBar.Visible = True

        ' 更新状态
        'Dim statusLabel As ToolStripStatusLabel = DirectCast(Me.Controls.Find("statusLabel", True)(0), ToolStripStatusLabel)
        'statusLabel.Text = If(isUpdate, "正在更新索引...", "正在创建索引...")
        statusLabel.Text = If(op = IndexOperation.Update, "正在更新索引...", If(op = IndexOperation.AddOrUpdate, "正在添加/更新索引...", "正在创建索引..."))

        ' 开始后台索引创建
        Dim indexingParams As New IndexingParams(rootDirectory, op) 'isUpdate)
        _indexBackgroundWorker.RunWorkerAsync(indexingParams)
    End Sub

    Private Sub IndexBackgroundWorker_DoWork(sender As Object, e As DoWorkEventArgs)
        Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
        Dim params As IndexingParams = DirectCast(e.Argument, IndexingParams)

        ' 添加用于存储进度信息的字段
        'e.Result = New IndexingResultWithProgress(params.IsUpdate)
        e.Result = New IndexingResultWithProgress(params.Operation = IndexOperation.Update OrElse params.Operation = IndexOperation.AddOrUpdate)
        Dim result = DirectCast(e.Result, IndexingResultWithProgress)

        ' 创建进度报告对象
        Dim progress As New Progress(Of IndexingProgressInfo)(
            Sub(info)
                ' 保存最新的进度信息
                result.LastProgressInfo = info
                ' 报告进度百分比
                worker.ReportProgress(info.PercentComplete, info)
            End Sub)

        '' 执行索引操作
        'If params.IsUpdate Then
        '    Dim count = _indexBuilder.UpdateIndexAsync(params.RootDirectory, progress).Result
        '    e.Result = New IndexingResult(params.IsUpdate, count)
        'Else
        '    Dim count = _indexBuilder.BuildIndexAsync(params.RootDirectory, progress).Result
        '    e.Result = New IndexingResult(params.IsUpdate, count)
        'End If

        ' 执行索引操作
        Try
            ' 选择 Build/Update/AddOrUpdate 的分支调用
            Select Case params.Operation
                Case IndexOperation.Build
                    result.ProcessedFiles = _indexBuilder.BuildIndexAsync(params.RootDirectory, progress).Result
                Case IndexOperation.Update
                    result.ProcessedFiles = _indexBuilder.UpdateIndexForRootAsync(params.RootDirectory, progress).Result
                Case IndexOperation.AddOrUpdate
                    result.ProcessedFiles = _indexBuilder.AddOrUpdateRootAsync(params.RootDirectory, progress).Result
            End Select

        Catch ex As OperationCanceledException
            e.Cancel = True
        End Try
        'If params.IsUpdate Then
        '    result.ProcessedFiles = _indexBuilder.UpdateIndexAsync(params.RootDirectory, progress).Result
        'Else
        '    result.ProcessedFiles = _indexBuilder.BuildIndexAsync(params.RootDirectory, progress).Result
        'End If

        e.Result = result
    End Sub

    Private Sub IndexBackgroundWorker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
        ' 若已标记不更新 UI，或控件已释放，直接返回 
        If _suppressUiUpdates OrElse Me.IsDisposed Then Return
        If progressBar Is Nothing OrElse progressBar.IsDisposed Then Return
        If statusLabel Is Nothing OrElse statusLabel.IsDisposed Then Return

        ' 隐藏进度条
        progressBar.Visible = False

        If e.Error IsNot Nothing Then
            MessageBox.Show("索引操作出错: " & e.Error.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error)
            statusLabel.Text = "索引操作失败"
            Return
        End If

        Dim result As IndexingResultWithProgress = DirectCast(e.Result, IndexingResultWithProgress)

        ' 更新状态
        'UpdateStatusInfo()

        ' 更新状态栏显示最终统计
        If result.LastProgressInfo IsNot Nothing Then
            statusLabel.Text = $"索引完成: 已处理 {result.LastProgressInfo.ProcessedFiles}/{result.LastProgressInfo.TotalFiles} 文件"
        Else
            statusLabel.Text = $"索引完成: 已处理 {result.ProcessedFiles} 个文件"
        End If

        ' 短暂延迟后更新为正常状态信息
        Task.Delay(3000).ContinueWith(Sub(t) Me.Invoke(Sub() UpdateStatusInfo()))

        ' 显示完成消息
        Dim message = If(result.IsUpdate,
                    $"索引更新完成，处理了 {result.ProcessedFiles} 个文件。",
                    $"索引创建完成，已索引 {result.ProcessedFiles} 个文件。")

        MessageBox.Show(message, "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information)
    End Sub

    Private Sub BackgroundWorker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
        ' 若已标记不再更新 UI，直接返回
        If _suppressUiUpdates Then Return
        '容器或控件已为空/ 已释放， 直接返回 
        If Me.IsDisposed Then Return
        If statusStrip Is Nothing OrElse statusStrip.IsDisposed Then Return
        If progressBar Is Nothing OrElse progressBar.IsDisposed Then Return
        ' 保护进度值范围
        Dim v = e.ProgressPercentage
        If v < progressBar.Minimum Then v = progressBar.Minimum
        If v > progressBar.Maximum Then v = progressBar.Maximum

        Try
            progressBar.Value = v
            If e.UserState IsNot Nothing AndAlso TypeOf e.UserState Is IndexingProgressInfo Then
                If statusLabel IsNot Nothing AndAlso Not statusLabel.IsDisposed Then
                    Dim info As IndexingProgressInfo = DirectCast(e.UserState, IndexingProgressInfo)
                    statusLabel.Text = $"正在索引: {info.ProcessedFiles}/{info.TotalFiles} 文件 ({v}%) | 当前: {info.CurrentFileName}"
                End If
            End If
        Catch ex As ObjectDisposedException
            ' 窗口/控件已处于释放中，忽略
        Catch ex As InvalidOperationException
            ' 句柄状态异常等，忽略
        End Try

        '' 更新进度条
        'progressBar.Value = e.ProgressPercentage

        '' 如果有自定义进度信息，更新状态栏文本
        'If e.UserState IsNot Nothing AndAlso TypeOf e.UserState Is IndexingProgressInfo Then
        '    Dim info As IndexingProgressInfo = DirectCast(e.UserState, IndexingProgressInfo)

        '    ' 更新状态栏文本，显示详细进度
        '    statusLabel.Text = $"正在索引: {info.ProcessedFiles}/{info.TotalFiles} 文件 ({e.ProgressPercentage}%) | 当前: {info.CurrentFileName}"
        'End If
    End Sub

    ' 辅助方法
    Private Function FormatFileSize(size As Long) As String
        If size < 1024 Then
            Return $"{size} B"
        ElseIf size < 1024 * 1024 Then
            Return $"{size / 1024:F1} KB"
        ElseIf size < 1024 * 1024 * 1024 Then
            Return $"{size / (1024 * 1024):F1} MB"
        Else
            Return $"{size / (1024 * 1024 * 1024):F1} GB"
        End If
    End Function

    Private Sub SearchMainForm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        LoadSettings()
        CheckTesseractData()
    End Sub

    Private Sub SearchMainForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
        _suppressUiUpdates = True
        ' 如果正在索引，尝试取消（即便你的索引目前未真正可取消，至少可以阻止 UI 更新）
        If _indexBackgroundWorker IsNot Nothing AndAlso _indexBackgroundWorker.IsBusy Then
            _indexBackgroundWorker.CancelAsync()
        End If
    End Sub

    Private Sub SearchMainForm_Closed(sender As Object, e As EventArgs) Handles Me.Closed
        If _indexBackgroundWorker IsNot Nothing Then
            RemoveHandler _indexBackgroundWorker.DoWork, AddressOf IndexBackgroundWorker_DoWork
            RemoveHandler _indexBackgroundWorker.ProgressChanged, AddressOf BackgroundWorker_ProgressChanged
            RemoveHandler _indexBackgroundWorker.RunWorkerCompleted, AddressOf IndexBackgroundWorker_RunWorkerCompleted
        End If
        If _searchBackgroundWorker IsNot Nothing Then
            RemoveHandler _searchBackgroundWorker.DoWork, AddressOf SearchBackgroundWorker_DoWork
            RemoveHandler _searchBackgroundWorker.ProgressChanged, AddressOf BackgroundWorker_ProgressChanged
            RemoveHandler _searchBackgroundWorker.RunWorkerCompleted, AddressOf SearchBackgroundWorker_RunWorkerCompleted
        End If
    End Sub
End Class

'索引操作类型
Public Enum IndexOperation
    Build          ' 清空并创建（现有的“创建索引...”）
    Update         ' 更新指定目录
    AddOrUpdate    ' 添加或更新指定目录（“添加索引...”）
End Enum

' 辅助类
Public Class SearchResults
    Public Property Query As String
    Public Property Results As List(Of SearchResult)

    Public Sub New(query As String, results As List(Of SearchResult))
        Me.Query = query
        Me.Results = results
    End Sub
End Class

Public Class IndexingParams
    Public Property RootDirectory As String
    'Public Property IsUpdate As Boolean
    Public Property Operation As IndexOperation

    'Public Sub New(rootDirectory As String, isUpdate As Boolean)
    Public Sub New(rootDirectory As String, operation As IndexOperation)
        Me.RootDirectory = rootDirectory
        'Me.IsUpdate = isUpdate
        Me.Operation = operation
    End Sub
End Class

Public Class IndexingResult
    Public Property IsUpdate As Boolean
    Public Property ProcessedFiles As Integer

    Public Sub New(isUpdate As Boolean, processedFiles As Integer)
        Me.IsUpdate = isUpdate
        Me.ProcessedFiles = processedFiles
    End Sub
End Class

' 扩展的索引结果类，包含最后的进度信息
Public Class IndexingResultWithProgress
    Inherits IndexingResult

    Public Property LastProgressInfo As IndexingProgressInfo

    Public Sub New(isUpdate As Boolean)
        MyBase.New(isUpdate, 0)
    End Sub
End Class

' 自定义进度报告类
Public Class IndexingProgressInfo
    Public Property PercentComplete As Integer
    Public Property ProcessedFiles As Integer
    Public Property TotalFiles As Integer
    Public Property CurrentFileName As String

    Public Sub New(percentComplete As Integer, processedFiles As Integer, totalFiles As Integer, currentFileName As String)
        Me.PercentComplete = percentComplete
        Me.ProcessedFiles = processedFiles
        Me.TotalFiles = totalFiles
        Me.CurrentFileName = currentFileName
    End Sub
End Class
